<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>角动量守恒动画（滑冰运动员模型）</title>
    <style>
        /* 画布样式：边框+居中 */
        #canvas {
            border: 2px solid #333;
            display: block;
            margin: 20px auto;
            background-color: #f9f9f9;
        }
        /* 控制区样式：居中+间距 */
        .controls {
            text-align: center;
            margin: 30px auto;
            font-family: Arial, sans-serif;
            font-size: 16px;
        }
        /* 输入框与按钮样式：统一大小+美化 */
        .controls input {
            width: 80px;
            padding: 5px;
            margin: 0 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .controls button {
            padding: 6px 20px;
            margin-left: 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .controls button:hover {
            background-color: #45a049;
        }
        /* 数据标注样式：画布上的数值显示 */
        .data-label {
            position: absolute;
            left: 50%;
            transform: translateX(-50%);
            font-family: Arial, sans-serif;
            font-size: 14px;
            color: #333;
            background-color: rgba(255,255,255,0.8);
            padding: 2px 8px;
            border-radius: 3px;
        }
    </style>
</head>
<body>
    <!-- 参数控制区：用户调节J1、ω1、J2 -->
    <div class="controls">
        <label for="j1">初始转动惯量 \( J_1 \) (kg·m²): </label>
        <input type="number" id="j1" value="2" step="0.1" min="0.1">

        <label for="omega1">初始角速度 \( \omega_1 \) (rad/s): </label>
        <input type="number" id="omega1" value="2" step="0.1" min="0.1">

        <label for="j2">收缩后转动惯量 \( J_2 \) (kg·m²): </label>
        <input type="number" id="j2" value="1" step="0.1" min="0.1">

        <button id="generate">生成/更新动画</button>
    </div>

    <!-- 动画画布：绘制转动过程 -->
    <canvas id="canvas" width="600" height="400"></canvas>

    <!-- 数据标注：显示角动量、角速度数值 -->
    <div class="data-label" style="top: 80px;">初始角动量 \( L_1 = J_1\omega_1 = \) <span id="l1">4</span> kg·m²/s</div>
    <div class="data-label" style="top: 110px;">收缩后角速度 \( \omega_2 = L/J_2 = \) <span id="omega2">4</span> rad/s</div>

    <script>
        // 1. 获取DOM元素
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d'); // 2D绘图上下文
        const generateBtn = document.getElementById('generate');
        const j1Input = document.getElementById('j1');
        const omega1Input = document.getElementById('omega1');
        const j2Input = document.getElementById('j2');
        const l1Label = document.getElementById('l1');
        const omega2Label = document.getElementById('omega2');

        // 2. 动画核心参数
        let j1 = 2;    // 初始转动惯量（默认值）
        let omega1 = 2;// 初始角速度（默认值）
        let j2 = 1;    // 收缩后转动惯量（默认值）
        let L = j1 * omega1; // 角动量（守恒量）
        let omega2 = L / j2; // 收缩后角速度

        let angle = 0; // 当前转动角度（弧度）
        const centerX = canvas.width / 2; // 转动中心X坐标（画布中心）
        const centerY = canvas.height / 2; // 转动中心Y坐标（画布中心）
        const radius1 = 120; // 初始转动半径（对应J1，半径大→J大）
        const radius2 = 60;  // 收缩后转动半径（对应J2，半径小→J小）

        // 3. 绘制函数：每一帧更新画面
        function draw() {
            // 清空画布（避免上一帧残留）
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // 3.1 绘制转动中心（转轴）
            ctx.beginPath();
            ctx.arc(centerX, centerY, 12, 0, Math.PI * 2); // 圆形转轴
            ctx.fillStyle = '#333'; // 颜色：深灰
            ctx.fill();
            ctx.fillText('转轴', centerX - 15, centerY + 30); // 文字标注

            // 3.2 绘制初始轨迹（虚线，对应J1）
            ctx.beginPath();
            ctx.arc(centerX, centerY, radius1, 0, Math.PI * 2); // 圆形轨迹
            ctx.strokeStyle = '#2196F3'; // 颜色：蓝色
            ctx.setLineDash([8, 4]); // 虚线样式（实线8px，空白4px）
            ctx.lineWidth = 2;
            ctx.stroke();
            ctx.fillText('初始轨迹（J1）', centerX + radius1 + 10, centerY); // 标注

            // 3.3 绘制收缩后轨迹（虚线，对应J2）
            ctx.beginPath();
            ctx.arc(centerX, centerY, radius2, 0, Math.PI * 2);
            ctx.strokeStyle = '#f44336'; // 颜色：红色
            ctx.setLineDash([8, 4]);
            ctx.lineWidth = 2;
            ctx.stroke();
            ctx.fillText('收缩后轨迹（J2）', centerX + radius2 + 10, centerY - 20); // 标注

            // 3.4 计算当前质点位置（根据角度判断在哪个轨迹上）
            const currentRadius = angle < Math.PI ? radius1 : radius2; // 前半圈在J1轨迹，后半圈切换到J2轨迹
            const x = centerX + Math.cos(angle) * currentRadius; // X坐标（三角函数计算）
            const y = centerY + Math.sin(angle) * currentRadius; // Y坐标

            // 3.5 绘制质点（代表滑冰运动员的“质量集中点”）
            ctx.beginPath();
            ctx.arc(x, y, 18, 0, Math.PI * 2);
            ctx.fillStyle = '#4CAF50'; // 颜色：绿色
            ctx.fill();
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 2;
            ctx.stroke();

            // 3.6 更新角度（根据当前轨迹的角速度）
            if (angle < Math.PI) {
                angle += omega1 * 0.02; // 初始轨迹：用ω1更新角度（0.02为时间步长，控制动画速度）
            } else {
                angle += omega2 * 0.02; // 收缩后轨迹：用ω2更新角度
                // 角度超过2π（一圈）后重置，实现循环转动
                if (angle > 2 * Math.PI) angle = 0;
            }

            // 3.7 循环调用draw函数，实现平滑动画
            requestAnimationFrame(draw);
        }

        // 4. 按钮点击事件：更新参数并重新计算
        generateBtn.addEventListener('click', () => {
            // 获取用户输入的参数（转为数字）
            j1 = parseFloat(j1Input.value);
            omega1 = parseFloat(omega1Input.value);
            j2 = parseFloat(j2Input.value);

            // 验证参数有效性（避免0或负数，导致计算错误）
            if (j1 <= 0 || omega1 <= 0 || j2 <= 0) {
                alert('转动惯量和角速度必须大于0！');
                return;
            }

            // 重新计算角动量和收缩后角速度
            L = j1 * omega1;
            omega2 = L / j2;

            // 更新页面上的数值标注
            l1Label.textContent = L.toFixed(1); // 保留1位小数
            omega2Label.textContent = omega2.toFixed(1);

            // 重置角度，重新开始动画
            angle = 0;
        });

        // 5. 初始加载时执行动画
        draw();
    </script>
</body>
</html>